#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>

#define SEP " "
#define SEP_LEN strlen(SEP)
#define SEP_LINE "\r\n"
#define SEP_LINE_LEN strlen(SEP_LINE)

using namespace std;

//client send request to server -> server recv and handler request to get response -> server send response to client
/*
客户端经过用户输入产生请求 将请求序列化为字节流数据 发送给服务器
服务器接收到字节流数据 反序列化为结构化数据进行处理 得到结构化的处理结果 将处理结果序列化后发送给客户端
客户端接收到处理结果后 反序列化为结构化数据 对结果进行打印

关键点: tcp C/S通信  协议定制  序列化反序列化  加报头去报头

在tcp C/S通信场景中 协议定制其实就是自定义一种客户端和服务器双方都要遵循的约定
例如当前场景中 Request/Response为双方统一的数据格式  "content_len"\r\n"content"\r\n为双方指定的报文格式
*/

//"profix(content_len)"\r\n"content"\r\n  profix报头内容为正文长度
bool recvPackage(int sockfd, string& inbuffer, string* out)
{
    char buffer[1024];
    while (true)
    {
        int n = recv(sockfd, buffer, sizeof buffer - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            inbuffer += buffer;
            int pos = inbuffer.find(SEP_LINE);
            if (pos == string::npos)//读取到的报文不完整 继续读取
                continue;
            string profix = inbuffer.substr(0, pos);
            int content_len = stoi(profix);
            int total_len = profix.size() + content_len + 2 * SEP_LINE_LEN;
            if (inbuffer.size() < total_len)//读取到的报文不完整 继续读取
                continue;
            *out = "";
            *out = inbuffer.substr(0, total_len);
            inbuffer.erase(0, total_len);
            break;
        }
        else
        {
            return false;
        }
    }
    return true;
}

//"content" -> "profix"\r\n"content"\r\n
void enLength(const string& in, string* out)
{
    int content_len = in.size();
    *out = "";
    *out += to_string(content_len);
    *out += SEP_LINE;
    *out += in;
    *out += SEP_LINE;
}

//"profix"\r\n"content"\r\n -> "content"
bool deLength(const string& in, string* out)
{
    int pos = in.find(SEP_LINE);
    if (pos == string::npos)
        return false;
    string profix = in.substr(0, pos);
    int content_len = stoi(profix);
    *out = "";
    *out = in.substr(pos + SEP_LINE_LEN, content_len);
    return true;
}

//字节流:"x op y"  结构化:int x, int y, char op
struct Request
{
    Request()
    {}

    //int x, int y, char op -> "x op y"
    void serialize(string* out)
    {
        *out = "";
        *out += to_string(_x);
        *out += SEP;
        *out += _op;
        *out += SEP;
        *out += to_string(_y);
    }

    //Request对象本身就是反序列化的结构化数据 所以反序列化就是往Request对象里填充属性值
    //"x op y" -> int x, int y, char op
    bool deserialize(const string& in)
    {
        int left = in.find(SEP);
        if (left == string::npos)//请求格式有误
            return false;
        int right = in.rfind(SEP);
        if (left + SEP_LEN + 1 != right)//请求格式有误
            return false;
        string x = in.substr(0, left);
        string y = in.substr(right + SEP_LEN, string::npos);
        if (x.empty() || y.empty())
            return false;
        _x = stoi(x);
        _y = stoi(y);
        _op = in[left + SEP_LEN];
        return true;
    }

    int _x;
    int _y;
    char _op;
};

//字节流:"exitcode result"  结构化:int exitcode, int result
struct Response
{
    Response()
    {}

    //int exitcode, int result -> "exitcode result"
    void serialize(string *out)
    {
        *out = "";
        *out += to_string(_exitcode);
        *out += SEP;
        *out += to_string(_result);
    }

    //"exitcode result" -> int exitcode, int result
    bool deserialize(const string& in)
    {
        int pos = in.find(SEP);
        if (pos == string::npos)
            return false;
        string exitcode = in.substr(0, pos);
        string result = in.substr(pos + SEP_LEN, string::npos);
        if (exitcode.empty() || result.empty())
            return false;
        _exitcode = stoi(exitcode);
        _result = stoi(result);
        return true;
    }

    int _exitcode;
    int _result;
};

typedef function<void(Request, Response*)> func_t;